package br.ufc.minijavaufc.visitor;

import br.ufc.minijavaufc.ast.AndExpression;
import br.ufc.minijavaufc.ast.ArrayAssignExpression;
import br.ufc.minijavaufc.ast.ArrayAssignStatement;
import br.ufc.minijavaufc.ast.ArrayInstantiationExpression;
import br.ufc.minijavaufc.ast.ArrayLengthExpression;
import br.ufc.minijavaufc.ast.ArrayLookupExpression;
import br.ufc.minijavaufc.ast.AssignStatement;
import br.ufc.minijavaufc.ast.BlockStatement;
import br.ufc.minijavaufc.ast.BooleanType;
import br.ufc.minijavaufc.ast.ClassDeclaration;
import br.ufc.minijavaufc.ast.ClassDeclarationList;
import br.ufc.minijavaufc.ast.Expression;
import br.ufc.minijavaufc.ast.ExpressionList;
import br.ufc.minijavaufc.ast.ExtendedClassDeclaration;
import br.ufc.minijavaufc.ast.FalseExpression;
import br.ufc.minijavaufc.ast.Formal;
import br.ufc.minijavaufc.ast.FormalList;
import br.ufc.minijavaufc.ast.FormalRest;
import br.ufc.minijavaufc.ast.Identifier;
import br.ufc.minijavaufc.ast.IdentifierExpression;
import br.ufc.minijavaufc.ast.IdentifierType;
import br.ufc.minijavaufc.ast.IfStatement;
import br.ufc.minijavaufc.ast.IntegerArrayType;
import br.ufc.minijavaufc.ast.IntegerInstantiationExpression;
import br.ufc.minijavaufc.ast.IntegerType;
import br.ufc.minijavaufc.ast.LessThanExpression;
import br.ufc.minijavaufc.ast.Literal;
import br.ufc.minijavaufc.ast.LiteralExpression;
import br.ufc.minijavaufc.ast.MainClass;
import br.ufc.minijavaufc.ast.MethodCallExpression;
import br.ufc.minijavaufc.ast.MethodDeclaration;
import br.ufc.minijavaufc.ast.MethodDeclarationList;
import br.ufc.minijavaufc.ast.MinusExpression;
import br.ufc.minijavaufc.ast.NotExpression;
import br.ufc.minijavaufc.ast.PlusExpression;
import br.ufc.minijavaufc.ast.Program;
import br.ufc.minijavaufc.ast.SimpleClassDeclaration;
import br.ufc.minijavaufc.ast.Statement;
import br.ufc.minijavaufc.ast.StatementList;
import br.ufc.minijavaufc.ast.SysoStatement;
import br.ufc.minijavaufc.ast.This;
import br.ufc.minijavaufc.ast.TimesExpression;
import br.ufc.minijavaufc.ast.TrueExpression;
import br.ufc.minijavaufc.ast.Type;
import br.ufc.minijavaufc.ast.VarDeclaration;
import br.ufc.minijavaufc.ast.VarDeclarationList;
import br.ufc.minijavaufc.ast.WhileStatement;
import br.ufc.minijavaufc.semantic.Error;

public class TypeVisitorImpl implements TypeVisitor {

	private Error error = new Error();

	@Override
	public Type visit(AndExpression andExpression) {
		if (!(andExpression.firstExpression.accept(this) instanceof BooleanType && andExpression.secondExpression.accept(this) instanceof BooleanType)) {
			error.complain("+ operand must be an integer");
		}
		return new BooleanType();
	}

	// TODO Isso parece não existir no .jj
	@Override
	public Type visit(ArrayAssignExpression arrayAssignExpression) {
		arrayAssignExpression.firsExpression.accept(this);
		arrayAssignExpression.secondExpression.accept(this);
		return new IntegerArrayType();
	}

	@Override
	public Type visit(ArrayAssignStatement arrayAssignStatement) {
		if (!(arrayAssignStatement.identifier.accept(this) instanceof IdentifierType)) {
			error.complain(arrayAssignStatement.identifier.id + " is not a valid identifier");
		}
		if (!(arrayAssignStatement.firstExpression.accept(this) instanceof IntegerType)) {
			error.complain("Expression inside brackets must be of integer type");
		}
		if (!(arrayAssignStatement.secondExpression.accept(this) instanceof IntegerType)) {
			error.complain("Expression at right must be of integer type");
		}
		return new IntegerArrayType();
	}

	@Override
	public Type visit(ArrayInstantiationExpression arrayInstantiationExpression) {
		if (!(arrayInstantiationExpression.expression.accept(this) instanceof IntegerType)) {
			error.complain("Expression must be an integer");
		}
		return new IntegerArrayType();
	}

	@Override
	public Type visit(ArrayLengthExpression arrayLengthExpression) {
		arrayLengthExpression.firstExpression.accept(this);
		return null;
	}

	@Override
	public Type visit(ArrayLookupExpression arrayLookupExpression) {
		arrayLookupExpression.firstExpression.accept(this);
		arrayLookupExpression.secondExpression.accept(this);
		return null;
	}

	@Override
	public Type visit(AssignStatement assignStatement) {
		assignStatement.identifier.accept(this);
		assignStatement.expression.accept(this);
		return null;
	}

	@Override
	public Type visit(BlockStatement blockStatement) {
		for (int i = 0; i < blockStatement.statementList.size(); i++) {
			blockStatement.statementList.getElement(i).accept(this);
		}
		return null;
	}

	@Override
	public Type visit(BooleanType booleanType) {
		return null;
	}

	@Override
	public Type visit(ClassDeclaration classDeclaration) {
		return null;
	}

	@Override
	public Type visit(ClassDeclarationList classDeclarationList) {
		return null;
	}

	@Override
	public Type visit(Expression expression) {
		return null;
	}

	@Override
	public Type visit(ExpressionList expressionList) {
		return null;
	}

	@Override
	public Type visit(ExtendedClassDeclaration extendedClassDeclaration) {
		extendedClassDeclaration.classIdentifier.accept(this);
		extendedClassDeclaration.extendedClassIdentifier.accept(this);
		for (int i = 0; i < extendedClassDeclaration.varDeclarationList.size(); i++) {
			extendedClassDeclaration.varDeclarationList.getElement(i);
		}
		for (int i = 0; i < extendedClassDeclaration.methodDeclarationList.size(); i++) {
			extendedClassDeclaration.methodDeclarationList.getElement(i);
		}
		return null;
	}

	@Override
	public Type visit(FalseExpression falseExpression) {
		return null;
	}

	@Override
	public Type visit(Formal formal) {
		formal.type.accept(this);
		formal.identifier.accept(this);
		return null;
	}

	@Override
	public Type visit(FormalList formalList) {
		return null;
	}

	@Override
	public Type visit(FormalRest formalRest) {
		formalRest.type.accept(this);
		formalRest.identifier.accept(this);
		return null;
	}

	@Override
	public Type visit(Identifier identifier) {
		return null;
	}

	@Override
	public Type visit(IdentifierExpression identifierExpression) {
		return null;
	}

	@Override
	public Type visit(IdentifierType identifierType) {
		return null;
	}

	@Override
	public Type visit(IfStatement ifStatement) {
		ifStatement.expression.accept(this);
		ifStatement.firstStatement.accept(this);
		ifStatement.secondStatement.accept(this);
		return null;
	}

	@Override
	public Type visit(IntegerArrayType integerArrayType) {
		return null;
	}

	@Override
	public Type visit(IntegerInstantiationExpression integerInstantiationExpression) {
		integerInstantiationExpression.identifier.accept(this);
		return null;
	}

	@Override
	public Type visit(IntegerType integerType) {
		return null;
	}

	@Override
	public Type visit(LessThanExpression lessThanExpression) {
		lessThanExpression.firstExpression.accept(this);
		lessThanExpression.secondExpression.accept(this);
		return null;
	}

	@Override
	public Type visit(Literal literal) {
		return null;
	}

	@Override
	public Type visit(LiteralExpression literalExpression) {
		return null;
	}

	@Override
	public Type visit(MainClass mainClass) {
		mainClass.classIdentifier.accept(this);
		mainClass.argIdentifier.accept(this);
		mainClass.statement.accept(this);
		return null;
	}

	@Override
	public Type visit(MethodCallExpression methodCallExpression) {
		methodCallExpression.firstExpression.accept(this);
		methodCallExpression.identifier.accept(this);
		for (int i = 0; i < methodCallExpression.expressionList.size(); i++) {
			methodCallExpression.expressionList.getElement(i);
		}
		return null;
	}

	@Override
	public Type visit(MethodDeclaration methodDeclaration) {
		methodDeclaration.type.accept(this);
		methodDeclaration.identifier.accept(this);
		for (int i = 0; i < methodDeclaration.formalList.size(); i++) {
			methodDeclaration.formalList.getElement(i).accept(this);
		}
		for (int i = 0; i < methodDeclaration.varDeclarationList.size(); i++) {
			methodDeclaration.varDeclarationList.getElement(i).accept(this);
		}
		for (int i = 0; i < methodDeclaration.statementList.size(); i++) {
			methodDeclaration.statementList.getElement(i);
		}
		methodDeclaration.expression.accept(this);
		return null;
	}

	@Override
	public Type visit(MethodDeclarationList methodDeclarationList) {
		return null;
	}

	@Override
	public Type visit(MinusExpression minusExpression) {
		minusExpression.firstExpression.accept(this);
		minusExpression.secondExpression.accept(this);
		return null;
	}

	@Override
	public Type visit(NotExpression notExpression) {
		notExpression.expression.accept(this);
		return null;
	}

	@Override
	public Type visit(PlusExpression plusExpression) {
		if (!(plusExpression.firstExpression.accept(this) instanceof IntegerType && plusExpression.secondExpression.accept(this) instanceof IntegerType)) {
			error.complain("+ operand must be an integer");
		}
		return new IntegerType();
	}

	@Override
	public Type visit(Program program) {
		program.mainClass.accept(this);
		for (int i = 0; i < program.classDeclarationList.size(); i++) {
			program.classDeclarationList.getElement(i).accept(this);
		}
		return null;
	}

	@Override
	public Type visit(SimpleClassDeclaration simpleClassDeclaration) {
		simpleClassDeclaration.classIdentifier.accept(this);
		for (int i = 0; i < simpleClassDeclaration.varDeclarationList.size(); i++) {
			simpleClassDeclaration.varDeclarationList.getElement(i).accept(this);
		}
		for (int i = 0; i < simpleClassDeclaration.methodDeclarationList.size(); i++) {
			simpleClassDeclaration.methodDeclarationList.getElement(i).accept(this);
		}
		return null;
	}

	@Override
	public Type visit(Statement statement) {
		return null;
	}

	@Override
	public Type visit(StatementList statementList) {
		return null;
	}

	@Override
	public Type visit(SysoStatement sysoStatement) {
		sysoStatement.expression.accept(this);
		return null;
	}

	@Override
	public Type visit(This thiz) {
		return null;
	}

	@Override
	public Type visit(TimesExpression timesExpression) {
		timesExpression.firstExpression.accept(this);
		timesExpression.secondExpression.accept(this);
		return null;
	}

	@Override
	public Type visit(TrueExpression trueExpression) {
		return null;
	}

	@Override
	public Type visit(Type type) {
		return null;
	}

	@Override
	public Type visit(VarDeclaration varDeclaration) {
		varDeclaration.type.accept(this);
		varDeclaration.identifier.accept(this);
		return null;
	}

	@Override
	public Type visit(VarDeclarationList varDeclarationList) {
		return null;
	}

	@Override
	public Type visit(WhileStatement whileStatement) {
		whileStatement.expression.accept(this);
		whileStatement.statement.accept(this);
		return null;
	}

}
